package servermanager;

/* This program simulates http server load balancing. */
/* http requests are made to the main server manager  */
/* which transfers the request to the least busiest   */
/* server.                                            */
/*  In the simulation, the user must enter the urls,  */
/* while there is a timer task that "services" the    */
/* requests by picking a random server and dequeuing  */
/* a request from it.                                 */
/* Since some of the output is being generated by     */
/* a timer, the text console is at times awaiting     */
/* user input, but output is repeated appearing on    */
/* the screen at the same time.                       */

import java.awt.event.*;
import javax.swing.Timer;

import java.lang.reflect.Array;
import java.util.*;

public class ServerManager {
	private final static String QUIT = "quit";
	private final static int MAX_SERVERS = 5;

	private final static int ONE_SECOND = 1000; // milliseconds to seconds
	private final static int REPEAT_INTERVAL = (2 * ONE_SECOND);

	private Queue<URL>[] httpServers;
	private Scanner input;

	private Timer timer;

	public static void main(String[] args) {
		new ServerManager();
	}

	/* This is the main event loop for the program. */
	/* Once the http servers and the timer are set up, */
	/* we keep asking the user for url input until they */
	/* indicate the program is to end. Their url */
	/* request is directed to the least busy server. */

	public ServerManager() {

		input = new Scanner(System.in);
		makeServerQueues();
		startServers();

		while (true) {
			URL nextURL = getURL();

			if (nextURL == null) {
				break;
			}

			Queue<URL> serverQueueToUse = getLeastBusyServerQueue();
			serverQueueToUse.add(nextURL);
			showServerQueueSizes();
		}
	}

	/* Create the server array and make a new server for each */
	/* array position. */

	private void makeServerQueues() {
		httpServers = (ArrayDeque<URL>[]) Array.newInstance(ArrayDeque.class,
				MAX_SERVERS);

		for (int serverIndex = 0; serverIndex < httpServers.length; serverIndex++) {
			httpServers[serverIndex] = new ArrayDeque<URL>();
		}
	}

	/* Obtain a URL from the user (keyboard) */

	private URL getURL() {
		String aLine;

		System.out.print("Enter a URL: ");

		if (!input.hasNextLine()) {
			return null;
		}

		aLine = input.nextLine();

		if (aLine.equals(QUIT)) {
			return null;
		}

		return new URL(aLine);
	}

	/* Go through the list of servers, finding the one with */
	/* the fewest queued items. */

	private Queue<URL> getLeastBusyServerQueue() {
		int leastBusyIndex = 0;
		int leastBusySize = httpServers[0].size();

		for (int serverIndex = 1; serverIndex < httpServers.length; serverIndex++) {
			int currentBusySize = httpServers[serverIndex].size();
			if (currentBusySize < leastBusySize) {
				leastBusySize = currentBusySize;
				leastBusyIndex = serverIndex;
			}
		}
		return httpServers[leastBusyIndex];
	}

	/* Show the current queue lengths for all the servers */

	private void showServerQueueSizes() {
		System.out.println("Server     Queue Length");
		for (int serverIndex = 0; serverIndex < httpServers.length; serverIndex++) {
			System.out.println(serverIndex + "                "
					+ httpServers[serverIndex].size());
		}
	}

	/* Start a timer task that will generate a random server number */
	/* and "process" an http request. */

	/* A timer task requires a repeat interval as the first */
	/* parameter, and a class with an action listener as the */
	/* second parameter. Each time the timer goes off, */
	/* the actionPerformed method for the class is called. */

	private void startServers() {
		timer = new Timer(REPEAT_INTERVAL, new RandomServe());
		timer.start();
	}

	/* The actionlistener for the timer. Random's nextInt returns */
	/* a random number within the given range. */

	class RandomServe implements ActionListener {
		Random rand;

		RandomServe() {
			rand = new Random();
		}

		public void actionPerformed(ActionEvent evt) {
			int serverDoneIndex = rand.nextInt(httpServers.length);
			if (httpServers[serverDoneIndex].size() != 0) { // server must have
															// work
				URL theURL = httpServers[serverDoneIndex].remove();
				System.out.println("Serving request: " + theURL
						+ " from server " + serverDoneIndex);
			}
		}
	}
}